package br.cefetpe.tsi.game.base.views;

import static java.lang.Math.cos;
import static java.lang.Math.sin;
import static java.lang.Math.toRadians;

import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;

public class HexagonShape implements Shape, PathIterator {
	private Point2D.Double center = new Point2D.Double(0, 0);

	private Point2D.Double p1 = null, p2 = null, p3 = null, p4 = null,
			p5 = null, p6 = null;

	private double h = 0, d = 0;

	private double radius = 50;

	public HexagonShape(Point2D.Double center, double radius) {
		setCenter(center);
		setRadius(radius);
		setH();
		setD();
		setP1(new Point2D.Double(center.getX() - getD(), center.getY() - getH()));
		setP2(new Point2D.Double(center.getX() - getRadius(), center.getY()));
		setP3(new Point2D.Double(center.getX() - getD(), center.getY() + getH()));
		setP4(new Point2D.Double(center.getX() + getD(), center.getY() + getH()));
		setP5(new Point2D.Double(center.getX() + getRadius(), center.getY()));
		setP6(new Point2D.Double(center.getX() + getD(), center.getY() - getH()));
	}

	public HexagonShape(Point2D.Double center) {
		setCenter(center);
		setH();
		setD();
		setP1(new Point2D.Double(center.getX() - getD(), center.getY() - getH()));
		setP2(new Point2D.Double(center.getX() - getRadius(), center.getY()));
		setP3(new Point2D.Double(center.getX() - getD(), center.getY() + getH()));
		setP4(new Point2D.Double(center.getX() + getD(), center.getY() + getH()));
		setP5(new Point2D.Double(center.getX() + getRadius(), center.getY()));
		setP6(new Point2D.Double(center.getX() + getD(), center.getY() - getH()));
	}

	/*
	 * Shape Implementation Methods
	 */
	public boolean contains(Point2D p) {
		boolean part1 = (p.getY() <= calculateA(getP1(), getP2()) * p.getX()
				+ calculateB(getP1(), getP2()));
		boolean part2 = (p.getY() <= calculateA(getP2(), getP3()) * p.getX()
				+ calculateB(getP1(), getP2()));
		boolean part3 = (p.getY() >= calculateA(getP3(), getP4()) * p.getX()
				+ calculateB(getP1(), getP2()));
		boolean part4 = (p.getY() >= calculateA(getP4(), getP5()) * p.getX()
				+ calculateB(getP1(), getP2()));
		boolean part5 = (p.getY() >= calculateA(getP5(), getP6()) * p.getX()
				+ calculateB(getP1(), getP2()));
		boolean part6 = (p.getY() <= calculateA(getP6(), getP1()) * p.getX()
				+ calculateB(getP1(), getP2()));

		if (part1 && part2 && part3 && part4 && part5 && part6) {
			return true;
		} else {
			return false;
		}
	}
	public static void main(String[] args) {
		HexagonShape hs = new HexagonShape(new Point2D.Double(10,10), 50);
		System.out.println(hs.contains(new Point(10, 10)));
		System.out.println(hs.contains(new Point(10, 1000)));
	}
	/*
	 * Shape Implementation Methods
	 */
	public boolean contains(Point p) {
		Triangle t;
		
		double dist = Point2D.distance(p.x, p.y, getCenter().x, getCenter().y);
		System.out.println("Center: "+ getCenter());
		System.out.println("Dist: " + dist);
		if (dist <= getH()) {
			return true;
		} else {
			return false;
		}

	}
//	public boolean contains(Point p) {
//		boolean part1 = (p.getY() <= calculateA(getP1(), getP2()) * p.getX()
//				+ calculateB(getP1(), getP2()));
//		boolean part2 = (p.getY() <= calculateA(getP2(), getP3()) * p.getX()
//				+ calculateB(getP1(), getP2()));
//		boolean part3 = (p.getY() >= calculateA(getP3(), getP4()) * p.getX()
//				+ calculateB(getP1(), getP2()));
//		boolean part4 = (p.getY() >= calculateA(getP4(), getP5()) * p.getX()
//				+ calculateB(getP1(), getP2()));
//		boolean part5 = (p.getY() >= calculateA(getP5(), getP6()) * p.getX()
//				+ calculateB(getP1(), getP2()));
//		boolean part6 = (p.getY() <= calculateA(getP6(), getP1()) * p.getX()
//				+ calculateB(getP1(), getP2()));
//		System.out.println("Parte 1 = "+ part1);
//		System.out.println("Parte 2 = "+ part2);
//		System.out.println("Parte 3 = "+ part3);
//		System.out.println("Parte 4 = "+ part4);
//		System.out.println("Parte 5 = "+ part5);
//		System.out.println("Parte 6 = "+ part1);
//		if (part1 && part2 && part3 && part4 && part5 && part6) {
//			return true;
//		} else {
//			return false;
//		}
//	}
	public boolean contains(Rectangle2D ret) {
		// The Rectangle2D class describes a rectangle defined by
		// a location (x, y) and dimension (w x h).
		Point2D p2dTopLeft = new Point2D.Double(ret.getX(), ret.getY());
		Point2D p2dTopRight = new Point2D.Double(ret.getX() + ret.getWidth(),
				ret.getY());
		Point2D p2dBottomLeft = new Point2D.Double(ret.getX(), ret.getY()
				+ ret.getHeight());
		Point2D p2dBottomRight = new Point2D.Double(
				ret.getX() + ret.getWidth(), ret.getY() + ret.getHeight());
		boolean isp2dTopLeft = contains(p2dTopLeft);
		boolean isp2dTopRight = contains(p2dTopRight);
		boolean isp2dBottomLeft = contains(p2dBottomLeft);
		boolean isp2dBottomRight = contains(p2dBottomRight);

		if (isp2dBottomRight && isp2dBottomLeft && isp2dTopRight
				&& isp2dTopLeft) {
			return true;
		} else {
			return false;
		}
	}

	public boolean contains(double x, double y) {
		boolean part1 = (y < calculateA(getP1(), getP2()) * x
				+ calculateB(getP1(), getP2()));
		boolean part2 = (y > calculateA(getP2(), getP3()) * x
				+ calculateB(getP1(), getP2()));
		boolean part3 = (y > calculateA(getP3(), getP4()) * x
				+ calculateB(getP1(), getP2()));
		boolean part4 = (y > calculateA(getP4(), getP5()) * x
				+ calculateB(getP1(), getP2()));
		boolean part5 = (y < calculateA(getP5(), getP6()) * x
				+ calculateB(getP1(), getP2()));
		boolean part6 = (y < calculateA(getP6(), getP1()) * x
				+ calculateB(getP1(), getP2()));

		if (part1 && part2 && part3 && part4 && part5 && part6) {
			return true;
		} else {
			return false;
		}
	}

	public boolean contains(double arg0, double arg1, double arg2, double arg3) {
		Point2D p2dTopLeft = new Point2D.Double(arg0, arg1);
		Point2D p2dTopRight = new Point2D.Double(arg0 + arg2, arg1);
		Point2D p2dBottomLeft = new Point2D.Double(arg0, arg1 + arg2);
		Point2D p2dBottomRight = new Point2D.Double(arg0 + arg2, arg1 + arg2);
		boolean isp2dTopLeft = contains(p2dTopLeft);
		boolean isp2dTopRight = contains(p2dTopRight);
		boolean isp2dBottomLeft = contains(p2dBottomLeft);
		boolean isp2dBottomRight = contains(p2dBottomRight);

		if (isp2dBottomRight && isp2dBottomLeft && isp2dTopRight
				&& isp2dTopLeft) {
			return true;
		} else {
			return false;
		}
	}

	public Rectangle getBounds() {
		Rectangle resultBounds = new Rectangle();
		resultBounds.x = (int) (getP1().x);
		resultBounds.y = (int) (getP6().y);
		resultBounds.width = (int) (getP4().x - getP2().x);
		resultBounds.height = (int) (getP3().y - getP6().y);
		return resultBounds;
	}

	public Rectangle2D getBounds2D() {
		Rectangle2D.Double resultBounds = new Rectangle2D.Double();
		resultBounds.x = (int) (getP1().x);
		resultBounds.y = (int) (getP6().y);
		resultBounds.width = (int) (getP4().x - getP2().x);
		resultBounds.height = (int) (getP3().y - getP6().y);
		return resultBounds;
	}

	public PathIterator getPathIterator(AffineTransform at) {
		GeneralPath gn = new GeneralPath();
		gn.moveTo((float) getP1().x, (float) getP1().y);
		gn.lineTo((float) getP2().x, (float) getP2().y);
		gn.lineTo((float) getP3().x, (float) getP3().y);
		gn.lineTo((float) getP4().x, (float) getP4().y);
		gn.lineTo((float) getP5().x, (float) getP5().y);
		gn.lineTo((float) getP6().x, (float) getP6().y);
		gn.lineTo((float) getP1().x, (float) getP1().y);
		return gn.getPathIterator(at);
	}

	public PathIterator getPathIterator(AffineTransform at, double arg1) {
		GeneralPath gn = new GeneralPath();
		gn.moveTo((float) getP1().x, (float) getP1().y);
		gn.lineTo((float) getP2().x, (float) getP2().y);
		gn.lineTo((float) getP3().x, (float) getP3().y);
		gn.lineTo((float) getP4().x, (float) getP4().y);
		gn.lineTo((float) getP5().x, (float) getP5().y);
		gn.lineTo((float) getP6().x, (float) getP6().y);
		gn.lineTo((float) getP1().x, (float) getP1().y);
		return gn.getPathIterator(at);
	}

	public boolean intersects(Rectangle2D ret) {
		Point2D p2dTopLeft = new Point2D.Double(ret.getX(), ret.getY());
		Point2D p2dTopRight = new Point2D.Double(ret.getX() + ret.getWidth(),
				ret.getY());
		Point2D p2dBottomLeft = new Point2D.Double(ret.getX(), ret.getY()
				+ ret.getHeight());
		Point2D p2dBottomRight = new Point2D.Double(
				ret.getX() + ret.getWidth(), ret.getY() + ret.getHeight());
		boolean isp2dTopLeft = contains(p2dTopLeft);
		boolean isp2dTopRight = contains(p2dTopRight);
		boolean isp2dBottomLeft = contains(p2dBottomLeft);
		boolean isp2dBottomRight = contains(p2dBottomRight);

		if (isp2dBottomRight || isp2dBottomLeft || isp2dTopRight
				|| isp2dTopLeft) {
			return true;
		} else {
			return false;
		}
	}

	public boolean intersects(double arg0, double arg1, double arg2, double arg3) {
		Point2D p2dTopLeft = new Point2D.Double(arg0, arg1);
		Point2D p2dTopRight = new Point2D.Double(arg0 + arg2, arg1);
		Point2D p2dBottomLeft = new Point2D.Double(arg0, arg1 + arg2);
		Point2D p2dBottomRight = new Point2D.Double(arg0 + arg2, arg1 + arg2);
		boolean isp2dTopLeft = contains(p2dTopLeft);
		boolean isp2dTopRight = contains(p2dTopRight);
		boolean isp2dBottomLeft = contains(p2dBottomLeft);
		boolean isp2dBottomRight = contains(p2dBottomRight);

		if (isp2dBottomRight || isp2dBottomLeft || isp2dTopRight
				|| isp2dTopLeft) {
			return true;
		} else {
			return false;
		}
	}

	/*
	 * PathIterator Implementation Methods
	 */

	public int currentSegment(float[] coords) {
		return getPathIterator(new AffineTransform()).currentSegment(coords);
	}

	public int currentSegment(double[] coords) {
		return getPathIterator(new AffineTransform()).currentSegment(coords);
	}

	public int getWindingRule() {
		return getPathIterator(new AffineTransform()).getWindingRule();
	}

	public boolean isDone() {
		return getPathIterator(new AffineTransform()).isDone();
	}

	public void next() {
		getPathIterator(new AffineTransform()).next();
	}

	public Point2D.Double getCenter() {
		return center;
	}

	public void setCenter(Point2D.Double center) {
		this.center = center;
	}

	public double getRadius() {
		return radius;
	}

	public void setRadius(double radius) {
		this.radius = radius;
	}

	public Point2D.Double getP1() {
		return p1;
	}

	public void setP1(Point2D.Double p1) {
		this.p1 = p1;
	}

	public Point2D.Double getP2() {
		return p2;
	}

	public void setP2(Point2D.Double p2) {
		this.p2 = p2;
	}

	public Point2D.Double getP3() {
		return p3;
	}

	public void setP3(Point2D.Double p3) {
		this.p3 = p3;
	}

	public Point2D.Double getP4() {
		return p4;
	}

	public void setP4(Point2D.Double p4) {
		this.p4 = p4;
	}

	public Point2D.Double getP5() {
		return p5;
	}

	public void setP5(Point2D.Double p5) {
		this.p5 = p5;
	}

	public Point2D.Double getP6() {
		return p6;
	}

	public void setP6(Point2D.Double p6) {
		this.p6 = p6;
	}

	private double getD() {
		return d;
	}

	private void setD() {
		this.d = getRadius() * cos(toRadians(60));
	}

	private double getH() {
		return h;
	}

	private void setH() {
		this.h = getRadius() * sin(toRadians(60));
	}

	/**
	 * a linear function can be characterized by its 2 arguments a and b (f(x) =
	 * ax + b) this function receives two Points (instance of Point2D.Double)
	 * and returns the a.
	 * 
	 * @return "a" parameter of a linear function
	 */
	private double calculateA(Point2D.Double point1, Point2D.Double point2) {
		double result = 0;
		result = (point2.getY() - point1.getY())
				/ (point2.getX() - point1.getX());
		return result;
	}

	/**
	 * a linear function can be characterized by its 2 arguments a and b (f(x) =
	 * ax + b) this function receives two Points (instance of Point2D.Double)
	 * and returns the b.
	 * 
	 * @return "b" parameter of a linear function
	 */
	private double calculateB(Point2D.Double point1, Point2D.Double point2) {
		double result = 0;
		result = point2.getY() - calculateA(point1, point2) * point2.getX();
		return result;
	}
}
